/**
    Implementation of an "infinite" blocking queue. This queue is intended to be used
    in a contest in which a working thread delegate the execution of a send to a communicator
    thread. Since we won't to violate the semantics of the send itself, the queue must be infinite.
    Clearly, its size will be (in most practical cases) limited. Thus, queue is blocking only when
    a reading operation is invoked and there are no element in the queue.
    The operations of insertion and extraction are mutually exclusive.

    The size of the queue grows dynamically, on the basis of the enqueue requests.
    It'is implemented by an array, handled as a circular buffer (Always Keep One Slot Open approach); when it
    is full, a new array will be created (with an incremented size) and the previous element will be
    re-organized on it.

    Why not linked list? too much memory allocation and no spatial locality.

    @author Tiziano De Matteis
*/


/* TODO (dematteis#1#): bisogna capire cosa inserire nella coda, se un puntatore all'oggetto o una copia totale */

/* TODO (dematteis#1#): Se fosse una coda di deleghe? si risparmierebbe una indirezione, ma sarebbe piu' grossa... */
#include <stdlib.h>
#include <stdio.h>
#include "../Include/shm_common.h"


#define INCR_STEP 10 //how much the size of the queue is incremented in the case that it's full
/**
    Initialization procedure for an empty queue of a given size.
    @param initialSize - the initial size of the queue. The actual size will be initialSize-1
    @return a pointer to the created queue
*/
queue_t *createQueue(int initialSize)
{
    queue_t *queue=(queue_t *)malloc(sizeof(queue_t));
    queue->readP=0;
    queue->writeP=0;
    queue->size=initialSize;
    queue->numElem=0;
    queue->elements=malloc(sizeof(void *)*initialSize);
    initLockVariable(&(queue->lv));
    initCondVariable(&(queue->cond));
    return queue;
}

/**
    Function for checking whether the queue is full or not
    @param q - the queue
    @return true if the queue is full
*/
inline bool isFull(queue_t *q)
{
    return (((q->writeP+1)%q->size)==(q->readP));
}

/**
    Function for checking whether the queue is empty or not
    @param q - the queue
    @return true if the queue is empty
*/
inline bool isEmpty(queue_t *q)
{
    return (q->readP==q->writeP);
}

/**
    Function for inserting an element in the queue
    TO BE COMPLETED
*/
void enqueue(queue_t *q, void *elem)
{
    spin_lock(&(q->lv));

    if(!isFull(q))
    {
        q->elements[q->writeP]=elem;
        q->writeP++;
        q->writeP%=q->size;
    }
    else
    {
        printf("enqueue: coda piena, rialloco\n");
        /*
            To increment the size we create a new array and we copy in it
            the old elements*/
        void **tmp=malloc(sizeof(void *)*q->size+INCR_STEP);
        int i=0;
        while(!isEmpty(q))
        {
            tmp[i++]=q->elements[q->readP];
            q->readP++;
            q->readP%=q->size;
        }
        //insert the new element
        tmp[i++]=elem;
        q->size+=INCR_STEP;
        free(q->elements);
        q->elements=tmp;
        //set the pointers
        q->writeP=i;
        q->readP=0;
    }
    //notify to the partner
    q->numElem++;
    notify(&(q->cond));
    spin_unlock(&(q->lv));
}

/**
    Function for extracting an element from the queue
    TO BE COMPLETED
*/
void *dequeue(queue_t *q){
    void *ret=NULL; //temp
    spin_lock(&(q->lv));
    while(isEmpty(q)) //we have to wait
    {
        //printf("dequeue: empty queue...wait\n");
        waitNotify(&(q->cond),&(q->lv));
    }

    //we are now sure that there is at least an element in the queue
    ret=q->elements[q->readP];
    q->readP++;
    q->readP%=q->size;
    q->numElem--;
    spin_unlock(&(q->lv));
    return ret;

}

/**
    Returns the number of element that are present in the queue
*/
inline int getActualSize(queue_t *q)
{
    return q->numElem;
}
/*
int main()
{
    #define SIZE 13
    int vect[]={1,2,3,4,5,6,7,8,9,10,11,12,13};
    int i;
    queue_t *q=createQueue(10);
    dequeue(q);
    enqueue(q,(void *)(&(vect[0])));
    printf("Estratto: %d\n",*((int *)dequeue(q)));
    for(i=0;i<SIZE;i++)
        enqueue(q,(void *)(&(vect[i])));
    for(i=0;i<SIZE;i++)
        printf("Estratto: %d\n",*((int *)dequeue(q)));
    return 0;
}
*/
